using System;
using System.Collections.Generic;
using System.Linq;
using Headsight.BPMN2WF.BPMN.Schemas;
using Headsight.BPMN2WF.BPMN.Utils;

namespace Headsight.BPMN2WF.BPMN.Model
{
    public class BPMNFlowNodesBlockFactory
    {
        private readonly BPMNProcessFactoryParams _bpmnMFacParams;

        // Currently under refactoring. This is not the intended final api...
        public BPMNFlowNodesBlockFactory()
            : this(new BPMNProcessFactoryParams(
                new BPMNSequenceFlowFactoryImpl(),
                new BPMNFlowNodeFactoryImpl(),
                new BPMNArgumentFactoryImpl()))
        {
        }

        /// <summary>
        /// Constructs a new <see cref="BPMN.BPMNFlowNodesBlockFactory"/> instance using the diversified factories passed in 
        /// by the <see cref="BPMNProcessFactoryParams"/> <para>bpmnModelFactoryParams</para>.
        /// </summary>
        public BPMNFlowNodesBlockFactory(BPMNProcessFactoryParams bpmnModelFactoryParams)
        {
            _bpmnMFacParams = bpmnModelFactoryParams;
        }

        /// <summary>
        /// Transforms the passed in <see cref="tDefinitions"/> in the <i>BPMN model representation</i> as 
        /// <i>needed</i> by <see cref="BPMN2WF"/>.
        /// </summary>
        public BPMNFlowNodesBlock CreateBPMNFlowNodesBlock(tDefinitions tDefinitions)
        {
            var tProcess = (tProcess)tDefinitions.Items.FirstOrDefault(x => typeof(tProcess).IsAssignableFrom(x.GetType()));

            var bpmnProcArgs = _bpmnMFacParams.BpmnModelFactory.CreateArguments(tDefinitions, tProcess, _bpmnMFacParams);
            var bpmnSeqFlows = _bpmnMFacParams.BpmnModelFactoryBpmnSequenceFlow.CreateBPMNSequenceFlows(tProcess, _bpmnMFacParams);
            var bpmnFlowNodes = _bpmnMFacParams.BpmnModelFactoryBpmnFlowNode.CreateBPMNFlowNodes(tProcess, bpmnSeqFlows, _bpmnMFacParams);

            var startNode = bpmnFlowNodes.FirstOrDefault(x => x.TElement is tStartEvent);


            var dicSeqFlow = new Dictionary<String, BPMNSequenceFlow>();
            var dicFlowNodes = new Dictionary<BPMNFlowNode, IList<BPMNFlowNode>>();

            bpmnSeqFlows.ForEach(x => dicSeqFlow.Add(x.Id, x));

            var tSeqFlows = tProcess.GetTSequenceFlows();

            foreach (var each in bpmnFlowNodes)
            {
                var ids = tSeqFlows.Where(x => x.sourceRef == each.Id).Select(x => x.targetRef).ToList();
                var bpmnFlowNode = each;// bpmnFlowNodes.First(x => x.Id == each.id);

                var flowNodes = (from eachId in ids
                                 where bpmnFlowNodes.Any(x => x.Id == eachId)
                                 select bpmnFlowNodes.First(x => x.Id == eachId)).ToList();

                dicFlowNodes[bpmnFlowNode] = flowNodes;
            }

            startNode = startNode ??
            bpmnFlowNodes.FirstOrDefault(x => dicFlowNodes.Values.Sum(each => each.Count(y => y.Id == x.Id)) == 0);

            //^^ under (heavy) refactoring... same for below... :-)
            var result = new BPMNFlowNodesBlock(startNode, bpmnProcArgs, dicFlowNodes, dicSeqFlow)
                             {FullQualifiedName = FullQualifiedName(tProcess)};


            return result;
        }

        private static String FullQualifiedName(tProcess tProcess)
        {
            var splitted = tProcess.id.Split(new[] { "." }, StringSplitOptions.None);

            for (var i = 0; i < splitted.Length; i++)
            {
                splitted[i] = splitted[i].Substring(0, 1).ToUpper() +
                              splitted[i].Substring(1, splitted[i].Length - 1);
            }

            return String.Join(".", splitted);
        }
    }
}